package com.wzq.tools.date;

import com.wzq.tools.constant.DateConstant;
import com.wzq.tools.exception.BizErrorCodeEnum;
import com.wzq.tools.exception.BizException;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

/**
 * 日期处理工具类（线程安全）
 * @author weizq
 * @since 2021/2/24
 */
public class DateHandle {
    private static final ThreadLocal<DateFormat> THREAD_LOCAL = new ThreadLocal<DateFormat>();

    /**
     * 通过格式获取格式化对象,线程安全
     *
     * @param datePattern 日期格式
     * @return java.text.DateFormat
     * @author weizq
     * @since 2021/2/24
     */
    public static DateFormat getDateFormat(String datePattern) throws BizException {
        if (StringUtils.isBlank(datePattern)) {
            throw new BizException(BizErrorCodeEnum.REQUEST_ERROR);
        }

        DateFormat fmt = THREAD_LOCAL.get();
        if (fmt == null) {
            fmt = new SimpleDateFormat(datePattern);
        }
        return fmt;
    }

    /**
     * 将字符串日期转换成Date对象
     *
     * @param dateStr     字符串日期对象
     * @param datePattern 日期格式
     * @return java.util.Date
     * @throws Exception 参数异常
     * @author weizq
     * @since 2021/2/24
     */
    public static Date parse(String dateStr, String datePattern) throws Exception {
        if (StringUtils.isBlank(datePattern) || StringUtils.isBlank(dateStr)) {
            throw new BizException(BizErrorCodeEnum.REQUEST_ERROR);
        }
        return getDateFormat(datePattern).parse(dateStr);
    }

    /**
     * 将Date对象转换成字符串日期
     *
     * @param date        日期对象
     * @param datePattern 日期格式
     * @return java.lang.String
     * @author weizq
     * @since 2021/2/24
     */
    public static String format(Date date, String datePattern) throws BizException {
        if (StringUtils.isBlank(datePattern) || date == null) {
            throw new BizException(BizErrorCodeEnum.REQUEST_ERROR);
        }
        return getDateFormat(datePattern).format(date);
    }

    /**
     * 取得特定时间对应的字符串,格式化为yyyy-MM-dd HH:mm:ss
     *
     * @param date 日期对象
     * @return java.lang.String
     * @author weizq
     * @since 2021/2/24
     */
    public static String ymdhmsFormat(Date date) throws BizException {
        if (date == null) {
            throw new BizException(BizErrorCodeEnum.REQUEST_ERROR);
        }
        return format(date, DateConstant.dtLong);
    }

    /**
     * 取得特定时间对应的字符串,格式化为yyyy-MM-dd
     *
     * @param date 日期对象
     * @return java.lang.String
     * @author weizq
     * @since 2021/2/24
     */
    public static String ymdFormat(Date date) throws BizException {
        if (date == null) {
            throw new BizException(BizErrorCodeEnum.REQUEST_ERROR);
        }
        return format(date, DateConstant.dtShort);
    }

    /**
     * 根据日期,得到当前年月
     *
     * @param date 日期对象
     * @return java.lang.String
     * @author weizq
     * @since 2021/2/24
     */
    public static final String ymFormat(Date date) throws BizException {
        if (date == null) {
            throw new BizException(BizErrorCodeEnum.REQUEST_ERROR);
        }
        return format(date, DateConstant.yyyyMM);
    }


    /**
     * 根据日期,得到当前年份
     *
     * @param date 日期对象
     * @return java.lang.String
     * @author weizq
     * @since 2021/2/25
     */
    public static final String yFormat(Date date) throws BizException {
        if (date == null) {
            throw new BizException(BizErrorCodeEnum.REQUEST_ERROR);
        }
        return format(date, DateConstant.yyyy);
    }

    /**
     * 把字符串形式转换成日期形式，字符串的格式必须为yyyy-MM-dd
     *
     * @param ymdStringDate 日期格式
     * @return java.util.Date
     * @throws Exception :参数异常或者格式化日期异常
     * @author weizq
     * @since 2021/2/24
     */
    public static final Date ymdString2Date(String ymdStringDate) throws Exception {
        if (ymdStringDate == null) {
            throw new BizException(BizErrorCodeEnum.REQUEST_ERROR);
        }
        return parse(ymdStringDate, DateConstant.dtShort);
    }

    /**
     * 把字符串形式转换成日期形式，字符串的格式必须为yyyy-MM-dd HH:mm:ss
     *
     * @param ymdhmsStringDate 日期格式
     * @return java.util.Date
     * @throws Exception :参数异常或者格式化日期异常
     * @author weizq
     * @since 2021/2/24
     */
    public static final Date ymdhmsString2Date(String ymdhmsStringDate) throws Exception {
        if (ymdhmsStringDate == null) {
            throw new BizException(BizErrorCodeEnum.REQUEST_ERROR);
        }
        return parse(ymdhmsStringDate, DateConstant.dtLong);
    }

    /**
     * 得到当前时间,把日期后的时间归0 变成(yyyy-MM-dd 00:00:00:000)
     *
     * @return java.util.Date
     * @author weizq
     * @since 2021/2/24
     */
    public static Date getCurrentDate() {
        Date date = new Date();
        return zerolizedTime(date);
    }

    /**
     * 把日期后的时间归0 变成(yyyy-MM-dd 00:00:00:000)
     *
     * @param fullDate 日期对象
     * @return java.util.Date
     * @author weizq
     * @since 2021/2/24
     */
    public static final Date zerolizedTime(Date fullDate) throws BizException {
        if (fullDate == null) {
            throw new BizException(BizErrorCodeEnum.REQUEST_ERROR);
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(fullDate);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return cal.getTime();
    }

    /**
     * 取得指定日期的星期数
     *
     * @param date 日期对象
     * @return java.lang.String
     * @author weizq
     * @since 2021/2/25
     */
    public static final String getWeek(Date date) {
        if (date == null) {
            throw new BizException(BizErrorCodeEnum.REQUEST_ERROR);
        }
        return format(date, DateConstant.week);
    }

    /**
     * 判断两个日期字符串是否相等,格式必需为yyyy-MM-dd
     *
     * @param one 第一个日期字符串
     * @param two 第二个日期字符串
     * @return boolean
     * @throws Exception :参数异常或者格式化日期异常
     * @author weizq
     * @since 2021/2/24
     */
    public static final boolean isEqual(String one, String two) throws Exception {
        if (StringUtils.isBlank(one) || StringUtils.isBlank(two)) {
            throw new BizException(BizErrorCodeEnum.REQUEST_ERROR);
        }
        return ymdString2Date(one).equals(ymdString2Date(two));
    }

    /**
     * 判断两个日期字符串是否相等
     *
     * @param one         第一个日期字符串
     * @param two         第二个日期字符串
     * @param datePattern 包含日期格式的字符串
     * @return boolean
     * @throws Exception :参数异常或者格式化日期异常
     * @author weizq
     * @since 2021/2/24
     */
    public static final boolean isEqual(String one, String two, String datePattern) throws Exception {
        if (StringUtils.isBlank(one) || StringUtils.isBlank(two) || StringUtils.isBlank(datePattern)) {
            throw new BizException(BizErrorCodeEnum.REQUEST_ERROR);
        }
        return isEqual(one, two, datePattern, datePattern);
    }

    /**
     * 判断两个日期字符串是否相等
     *
     * @param one            第一个日期字符串
     * @param two            第二个日期字符串
     * @param datePatternOne 对应第一个日期字符串的包含日期格式的字符串
     * @param datePatternTwo 对应第二个日期字符串的包含日期格式的字符串
     * @return java.lang.Boolean
     * @throws Exception :参数异常或者格式化日期异常
     * @author weizq
     * @since 2021/2/24
     */
    public static final Boolean isEqual(String one, String two, String datePatternOne, String datePatternTwo) throws Exception {
        if (StringUtils.isBlank(one) || StringUtils.isBlank(two) || StringUtils.isBlank(datePatternOne) || StringUtils.isBlank(datePatternTwo)) {
            throw new BizException(BizErrorCodeEnum.REQUEST_ERROR);
        }
        return parse(one, datePatternOne).equals(parse(two, datePatternTwo));
    }

    /**
     * 取得本月第一天时间
     *
     * @param date 日期对象
     * @return java.util.Date
     * @author weizq
     * @since 2021/2/24
     */
    public static final Date getFirstDayOfMonth(Date date) throws BizException {
        if (date == null) {
            throw new BizException(BizErrorCodeEnum.REQUEST_ERROR);
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.DAY_OF_MONTH, 1);
        return cal.getTime();
    }

    /**
     * 得到本月的最后一天
     *
     * @return java.lang.String
     * @author weizq
     * @since 2021/2/24
     */
    public static final String getMonthLastDay() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        return DateHandle.format(calendar.getTime(), DateConstant.dtSimple);
    }

    /**
     * 获取具体月份的第一天
     *
     * @param date      日期对象
     * @param monthDiff 具体月份
     * @return java.util.Date
     * @author weizq
     * @since 2021/2/24
     */
    public static final Date getMonthStartTime(Date date, int monthDiff) throws BizException {
        if (date == null || monthDiff > 12 || monthDiff < 1) {
            throw new BizException(BizErrorCodeEnum.REQUEST_ERROR);
        }
        Calendar calendar = GregorianCalendar.getInstance();
        if (date != null) {
            calendar.setTime(date);
        } else {
            calendar.setTimeInMillis(System.currentTimeMillis());
        }
        if (monthDiff != 0) {
            calendar.add(Calendar.MONTH, monthDiff);
        }
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        Date resultDate = calendar.getTime();
        return resultDate;
    }

    /**
     * 获取具体月份的最后一天
     *
     * @param date      日期对象
     * @param monthDiff 具体月份
     * @return java.util.Date
     * @author weizq
     * @since 2021/2/24
     */
    public static final Date getMonthEndTime(Date date, int monthDiff) {
        if (monthDiff > 12 || monthDiff < 1 || date == null) {
            throw new BizException(BizErrorCodeEnum.REQUEST_ERROR);
        }
        Calendar calendar = GregorianCalendar.getInstance();
        if (date != null) {
            calendar.setTime(date);
        } else {
            calendar.setTimeInMillis(System.currentTimeMillis());
        }
        if (monthDiff != 0) {
            calendar.add(Calendar.MONTH, monthDiff);
        }
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        Date resultDate = calendar.getTime();
        return resultDate;
    }

    /**
     * 加时间
     *
     * @param interval 增加项，可以是天数、月份、年数、时间、分钟、秒
     * @param date     时间
     * @param num      加的数目
     * @return java.util.Date
     * @author weizq
     * @since 2021/2/24
     */
    public static final Date dateAdd(int interval, Date date, int num) throws BizException {
        if (date == null) {
            throw new BizException(BizErrorCodeEnum.REQUEST_ERROR);
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        switch (interval) {
            case DateConstant.DATE_INTERVAL_DAY:
                calendar.add(Calendar.DATE, num);
                break;
            case DateConstant.DATE_INTERVAL_WEEK:
                calendar.add(Calendar.WEEK_OF_MONTH, num);
                break;
            case DateConstant.DATE_INTERVAL_MONTH:
                calendar.add(Calendar.MONTH, num);
                break;
            case DateConstant.DATE_INTERVAL_YEAR:
                calendar.add(Calendar.YEAR, num);
                break;
            case DateConstant.DATE_INTERVAL_HOUR:
                calendar.add(Calendar.HOUR, num);
                break;
            case DateConstant.DATE_INTERVAL_MINUTE:
                calendar.add(Calendar.MINUTE, num);
                break;
            case DateConstant.DATE_INTERVAL_SECOND:
                calendar.add(Calendar.SECOND, num);
                break;
            default:
        }
        return calendar.getTime();
    }

    /**
     * 两个时间时间差[前面时间和比较时间比,小于比较时间返回负数]
     *
     * @param interval 比较项，可以是天数、月份、年数、时间、分钟、秒
     * @param date     时间
     * @param compare  比较的时间
     * @return double
     * @author weizq
     * @since 2021/2/24
     */
    public static final double getDateDiff(int interval, Date date, Date compare) throws BizException {
        if (date == null || compare == null) {
            throw new BizException(BizErrorCodeEnum.REQUEST_ERROR);
        }
        double result = 0;
        double time = 0;
        Calendar calendar = null;
        switch (interval) {
            case DateConstant.DATE_INTERVAL_DAY:
                time = date.getTime() - compare.getTime();
                result = time / 1000d / 60d / 60d / 24d;
                break;
            case DateConstant.DATE_INTERVAL_HOUR:
                time = date.getTime() - compare.getTime();
                result = time / 1000d / 60d / 60d;
                break;
            case DateConstant.DATE_INTERVAL_MINUTE:
                time = date.getTime() / 1000d / 60d;
                result = time - compare.getTime() / 1000d / 60d;
                break;
            case DateConstant.DATE_INTERVAL_MONTH:
                calendar = Calendar.getInstance();
                calendar.setTime(date);
                time = calendar.get(Calendar.YEAR) * 12d;
                calendar.setTime(compare);
                time -= calendar.get(Calendar.YEAR) * 12d;
                calendar.setTime(date);
                time += calendar.get(Calendar.MONTH);
                calendar.setTime(compare);
                result = time - calendar.get(Calendar.MONTH);
                break;
            case DateConstant.DATE_INTERVAL_SECOND:
                time = date.getTime() - compare.getTime();
                result = time / 1000d;
                break;
            case DateConstant.DATE_INTERVAL_WEEK:
                calendar = Calendar.getInstance();
                calendar.setTime(date);
                time = calendar.get(Calendar.YEAR) * 52d;
                calendar.setTime(compare);
                time -= calendar.get(Calendar.YEAR) * 52d;
                calendar.setTime(date);
                time += calendar.get(Calendar.WEEK_OF_YEAR);
                calendar.setTime(compare);
                result = time - calendar.get(Calendar.WEEK_OF_YEAR);
                break;
            case DateConstant.DATE_INTERVAL_YEAR:
                calendar = Calendar.getInstance();
                calendar.setTime(date);
                time = calendar.get(Calendar.YEAR);
                calendar.setTime(compare);
                result = time - (double) calendar.get(Calendar.YEAR);
                break;
            default:
                break;
        }
        return new BigDecimal(result).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 获取时间差[前面时间和比较时间比,小于比较时间返回负数]
     *
     * @param level   返回时间等级(1:返回天;2:返回天-小时;3:返回天-小时-分4:返回天-小时-分-秒;)
     * @param date    时间
     * @param compare 比较的时间
     * @return java.lang.String 时间差(保留两位小数点, 小数点以后两位四舍五入)
     * @author weizq
     * @since 2021/2/24
     */
    public static final String getDateBetween(Integer level, Date date, Date compare) throws BizException {
        if (date == null || compare == null) {
            throw new BizException(BizErrorCodeEnum.REQUEST_ERROR);
        }
        long s = new BigDecimal(getDateDiff(DateConstant.DATE_INTERVAL_SECOND, date, compare)).setScale(2, BigDecimal.ROUND_HALF_UP).longValue();
        int ss = 1;
        int mi = ss * 60;
        int hh = mi * 60;
        int dd = hh * 24;

        long day = s / dd;
        long hour = (s - day * dd) / hh;
        long minute = (s - day * dd - hour * hh) / mi;
        long second = (s - day * dd - hour * hh - minute * mi) / ss;
        String flag = (day < 0 || hour < 0 || minute < 0 || second < 0) ? "-" : "";
        day = Math.abs(day);
        hour = Math.abs(hour);
        minute = Math.abs(minute);
        second = Math.abs(second);
        StringBuilder result = new StringBuilder(flag);
        switch (level) {
            case 1:
                if (day != 0) {
                    result.append(day).append("天");
                }
                break;
            case 2:
                if (day != 0) {
                    result.append(day).append("天");
                }
                if (hour != 0) {
                    result.append(hour).append("小时");
                }
                break;
            case 3:
                if (day != 0) {
                    result.append(day).append("天");
                }
                if (hour != 0) {
                    result.append(hour).append("小时");
                }
                if (minute != 0) {
                    result.append(minute).append("分");
                }
                break;
            case 4:
                if (day != 0) {
                    result.append(day).append("天");
                }
                if (hour != 0) {
                    result.append(hour).append("小时");
                }
                if (minute != 0) {
                    result.append(minute).append("分");
                }
                if (second != 0) {
                    result.append(second).append("秒");
                }
                break;
            default:
                break;
        }
        return result.toString();
    }

    /**
     * 时间是否合法
     *
     * @param date       日期对象
     * @param dateFormat 日期格式
     * @return boolean
     * @author weizq
     * @since 2021/2/24
     */
    public static final boolean isValidDate(String date, String dateFormat) {
        try {
            DateHandle.parse(date, dateFormat);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 计算两个日期的几分之几处，列如获取两个日期的三分之二日期等
     * <p>
     * 参数为空 返回空，请做好判空
     *
     * @param begin       开始日期
     * @param end         结束日期
     * @param numerator   分子
     * @param denominator 分母
     * @return java.util.Date
     * @author weizq
     * @since 2021/2/23
     */
    public static final Date dateWhatThePercentage(Date begin, Date end, Integer numerator, Integer denominator) throws BizException {
        if (!ObjectUtils.allNotNull(begin, end, numerator, denominator) && denominator > 0) {
            throw new BizException(BizErrorCodeEnum.REQUEST_ERROR);
        }
        return new Date((begin.getTime() + numerator * end.getTime()) / denominator);
    }
}

